package com.ywicc.commlibs.bluetooth;

import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.util.Log;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * author : zhouxuan77
 * e-mail : zhouxuan77@163.com
 * time   : 2022/04/01
 * desc   : 蓝牙相关用法封装
 * version: 1.0
 */
public class BluetoothUtils {

    private final static String TAG = "BluetoothUtils";

    // 自定义 UUID
    private final static String BLUETOOTH_UUID = "00001101-0000-1000-8000-00805F9B34FB";

    public final static int BLUETOOTH_SERVER = 1;
    public final static int BLUETOOTH_CLIENT = 2;

    private boolean isStart = false;
    private boolean isServerAccept = false;
    private boolean isServerStart = false;
    private boolean isClientStart = false;

    private String strBluetoothName;

    private BluetoothSocket serverSocket;
    private BluetoothSocket clientSocket;

    private BluetoothAdapter bluetoothAdapter;

    private InputStream clientInputStream;
    private InputStream serverInputStream;
    private OutputStream clientOutputStream;
    private OutputStream serverOutputStream;

    private final ThreadPoolExecutor threadPool;

    public BluetoothUtils() {
        // 创建对应的线程池
        threadPool = new ThreadPoolExecutor(4, 5, 0L,
                TimeUnit.MICROSECONDS, new ArrayBlockingQueue<>(2), new ThreadPoolExecutor.CallerRunsPolicy());

        Log.v(TAG, "ThreadPoolExecutor init");
    }

    /**
     * 初始化蓝牙适配器
     * <p>
     * 初始化蓝牙，获取 BluetoothAdapter
     *
     * @throws Exception 抛出异常:
     *                   1. Bluetooth Not Support: 当前设备不支持蓝牙
     *                   2. Bluetooth Not Start: 设备蓝牙未开启
     */
    @SuppressLint("HardwareIds")
    public void initBluetooth() throws Exception {
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

        // 判断是否支持蓝牙
        if (bluetoothAdapter == null) {
            throw new BluetoothUtilsException("Bluetooth Not Support");
        }

        // 判断蓝牙是否开启
        if (!bluetoothAdapter.isEnabled()) {
            throw new BluetoothUtilsException("Bluetooth Not Start");
        }

        isStart = true;

        strBluetoothName = bluetoothAdapter.getName();

        Log.v(TAG, "BluetoothAdapter init: " + strBluetoothName);
    }

    /**
     * 获取蓝牙已配对设别列表
     *
     * @return 返回蓝牙已配对设备列表
     */
    public Set<BluetoothDevice> getPairedDevice() {
        Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();

        Log.w(TAG, "Devices: " + pairedDevices.size());

        if (pairedDevices.size() > 0) {
            return pairedDevices;
        }

        return new HashSet<BluetoothDevice>();
    }

    /**
     * 启动 Bluetooth Server
     * <p>
     * 默认使用子线程，获取 BluetoothSocket，并设置 Server 端的接口回调
     *
     * @param serverImpl BluetoothUtilsServerImpl 接口
     */
    public void onStartServer(BluetoothUtilsServerImpl serverImpl) {
        threadPool.execute(new BluetoothRunnable(() -> {
            try {
                startServerRunnable(serverImpl);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }));
    }

    /**
     * 停止 Bluetooth Server
     * <p>
     * 停止 Server 端的接口回调，并关闭数据接收
     */
    public void stopServer() {
        // 设置标志符为停止
        isServerAccept = false;
        isServerStart = false;

        // 关闭 Socket
        try {
            serverInputStream.close();
            serverOutputStream.close();

            serverSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 启动 Bluetooth Client 端
     * <p>
     * 默认使用子线程，通过服务端的 UUID 获取 BluetoothSocket
     *
     * @param device 需要配对的 BluetoothDevice 对象
     */
    public void onStartClient(BluetoothDevice device, BluetoothUtilsClientImpl clientImpl) {
        threadPool.execute(new BluetoothRunnable(() -> {
            try {
                startClientRunnable(device, clientImpl);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }));
    }

    /**
     * 停止 Bluetooth Client
     * <p>
     * 停止 Client 端的接口回调，并关闭数据接收哦
     */
    public void stopClient() {
        // 设置标志符
        isClientStart = false;

        try {
            clientInputStream.close();
            clientOutputStream.close();

            clientSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 发送指定数据
     * <p>
     * 使用指定的 BluetoothSocket 发送数据
     *
     * @param data 需要发送的数据
     * @param type 指定的 BluetoothSocket
     *             1. BLUETOOTH_SERVER: 服务端
     *             2. BLUETOOTH_CLIENT 客户端
     */
    public void onSendData(byte[] data, int type) {
        Log.w(TAG, "发送数据为: " + Arrays.toString(data));

        if (type == BLUETOOTH_SERVER) {
            threadPool.execute(new BluetoothRunnable(new BluetoothRunnableInterface() {
                @Override
                public void run() {
                    try {
                        sendServerDataRunnable(data);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }));
        }

        if (type == BLUETOOTH_CLIENT) {
            threadPool.execute(new BluetoothRunnable(new BluetoothRunnableInterface() {
                @Override
                public void run() {
                    try {
                        sendClientDataRunnable(data);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }));
        }
    }

    /* 内部方法区域 */

    private void startServerRunnable(BluetoothUtilsServerImpl serverImpl) throws Exception {
        // 判空
        if (!isStart) {
            throw new BluetoothUtilsException("Bluetooth Not Start");
        } else if (serverImpl == null) {
            throw new BluetoothUtilsException("BluetoothUtilsServerImpl is Null");
        }

        BluetoothServerSocket socket = bluetoothAdapter.listenUsingRfcommWithServiceRecord(strBluetoothName,
                UUID.fromString(BLUETOOTH_UUID));

        isServerAccept = true;

        while (isServerAccept) {
            Log.v(TAG, "Wait for accept");

            // 获取服务端 Socket
            serverSocket = socket.accept();

            // 获取 InputStream
            serverInputStream = serverSocket.getInputStream();

            isServerStart = true;

            // 接收数据
            while (isServerStart) {
                byte[] bytes = new byte[serverInputStream.available()];
                int lenBytes = serverInputStream.read(bytes);

                if (bytes.length > 0) {
                    Log.v(TAG, "bytes: " + Arrays.toString(bytes));
                    Log.v(TAG, "byte len: " + lenBytes);

                    serverImpl.callBackServerData(bytes);
                }
            }
        }
    }

    private void startClientRunnable(BluetoothDevice device, BluetoothUtilsClientImpl clientImpl) throws Exception {
        if (!isStart) {
            throw new BluetoothUtilsException("Bluetooth Not Start");
        } else if (clientImpl == null) {
            throw new BluetoothUtilsException("BluetoothUtilsClientImpl is Null");
        }

        // 通过 UUID，从 BluetoothDevice 获取 Socket
        clientSocket = device.createRfcommSocketToServiceRecord(UUID.fromString(BLUETOOTH_UUID));

        // 取消 BluetoothAdapter 的搜索
        bluetoothAdapter.cancelDiscovery();

        // 连接服务端
        clientSocket.connect();

        clientInputStream = clientSocket.getInputStream();

        isClientStart = true;

        while (isClientStart) {
            byte[] bytes = new byte[clientInputStream.available()];
            int lenBytes = clientInputStream.read(bytes);

            if (lenBytes > 0) {
                Log.v(TAG, "bytes: " + Arrays.toString(bytes));
                Log.v(TAG, "byte len: " + lenBytes);

                clientImpl.callBackClientData(bytes);
            }
        }
    }

    private void sendServerDataRunnable(byte[] data) throws Exception {
        if (serverSocket == null) {
            throw new BluetoothUtilsException("serverSocket is Null");
        }

        // 获取 OutputStream
        serverOutputStream = serverSocket.getOutputStream();

        // 发送对应数据
        serverOutputStream.write(data, 0, data.length);
        serverOutputStream.flush();
    }

    private void sendClientDataRunnable(byte[] data) throws Exception {
        if (clientSocket == null) {
            throw new BluetoothUtilsException("clientSocket is Null");
        }

        // 获取 OutputStream
        clientOutputStream = clientSocket.getOutputStream();

        // 发送对应数据
        clientOutputStream.write(data, 0, data.length);
        clientOutputStream.flush();
    }

    /* 接口区域 */

    public interface BluetoothUtilsServerImpl {
        void callBackServerData(byte[] data);
    }

    public interface BluetoothUtilsClientImpl {
        void callBackClientData(byte[] data);
    }

    /**
     * 实现自定义 Runnable 封装，避免内存泄漏
     */
    private interface BluetoothRunnableInterface {
        void run();
    }

    public static class BluetoothRunnable implements Runnable {

        private final WeakReference<BluetoothRunnableInterface> weakInterface;

        public BluetoothRunnable(BluetoothRunnableInterface runnableInterface) {
            this.weakInterface = new WeakReference<>(runnableInterface);
        }

        @Override
        public void run() {
            weakInterface.get().run();
        }
    }

    public static class BluetoothUtilsException extends Exception {

        public BluetoothUtilsException(String message) {
            super(message);

            Log.e(TAG, "BluetoothUtilsException: " + message);
        }
    }

}
